<template>
  <teleport to="#fx-dialog">
    <transition name="fx-transition-translateY">
      <div class="fx-dialog" :class="{'dialog-leave':dialogClose}" :style="{zIndex:zIndex+1}" v-if="currentVisible">
        <div class="fx-dialog-inner" :style="dialogInnerStyle" ref="dialogDom">
          <div class="fx-dialog-title" @mousedown.stop.prevent="dragMousedown" v-if="$slots.title||title!==''">
            <slot name="title">{{ title }}</slot>
            <div class="close" v-if="showClose" @click="closeDialog('close')">
              <i class="iconfont icon-multiply fs10"/>
            </div>
          </div>
          <div class="fx-dialog-content">
            <slot></slot>
          </div>
          <div class="fx-dialog-footer" v-if="$slots.footer">
            <slot name="footer"></slot>
          </div>
        </div>
      </div>
    </transition>
    <transition name="fx-transition-modal">
      <div class="fx-dialog-modal" v-if="currentVisible&&showModal" :style="{zIndex}" @click="closeDialog('modal')"></div>
    </transition>
  </teleport>
</template>

<script lang="ts">
import { computed, defineComponent, onBeforeUnmount, PropType, reactive, ref, SetupContext, watch } from 'vue'
import { getSize, getZIndex } from '@/base/hooks'
import { disableAllowScroll } from '@/base/utils'

export type DialogBeforeClose = (cb: () => void) => void

interface DialogProps {
  visible: boolean;
  title: string;
  top: string;
  width: string;
  showClose: boolean;
  showModal: boolean;
  closeOnModal: boolean;
  closeOnEscape: boolean;
  beforeClose: DialogBeforeClose;
}

export default defineComponent({
  name: 'FDialog',
  props: {
    // 显示
    visible: {
      type: Boolean as PropType<boolean>,
      default: false,
      required: true
    },
    // 距离顶部距离
    title: {
      type: String as PropType<string>,
      default: '提示'
    },
    // 距离顶部距离
    top: {
      type: String as PropType<string>,
      default: '10vh'
    },
    // 距离顶部距离
    width: {
      type: String as PropType<string>,
      default: '50vw'
    },
    // 是否显示关闭按钮
    showClose: {
      type: Boolean as PropType<boolean>,
      default: true
    },
    // 是否显示模态层
    showModal: {
      type: Boolean as PropType<boolean>,
      default: true
    },
    // 是否点击模态窗关闭
    closeOnModal: {
      type: Boolean as PropType<boolean>,
      default: true
    },
    // 是否Esc关闭
    closeOnEscape: {
      type: Boolean as PropType<boolean>,
      default: true
    },
    // 关闭前
    beforeClose: {
      type: Function as PropType<DialogBeforeClose>,
      default: null
    }
  },
  emits: ['update:visible'],
  setup (props: DialogProps, ctx: SetupContext) {
    // 动态定位层级索引
    const zIndex = getZIndex()
    // 弹窗是否关闭状态
    const dialogClose = ref<boolean>(false)
    // 当前显示/关闭弹窗状态
    const currentVisible = ref<boolean>(props.visible)
    // 弹窗dom
    const dialogDom = ref<HTMLElement>()

    // 弹窗组件样式
    const dialogInnerStyle = computed(() => {
      const winW = document.documentElement.clientWidth
      const width = getSize(props.width, 420)
      const top = getSize(props.top, 60)
      return {
        zIndex: getZIndex(),
        width: `${width}px`,
        top: `${top}px`,
        left: `${(winW - width) / 2}px`
      }
    })

    // 关闭弹窗
    const closeDialog = (type: 'modal' | 'esc' | 'close' | 'default' = 'default') => {
      if (type === 'modal' && !props.closeOnModal) return
      if (props.beforeClose && (type === 'modal' || type === 'esc' || type === 'close')) {
        props.beforeClose(() => {
          currentVisible.value = false
        })
        return
      }
      currentVisible.value = false
    }
    // Esc关闭
    const handleEscClose = (ev: KeyboardEvent) => {
      if (ev.code === 'Escape' && props.closeOnEscape) closeDialog('esc')
    }
    // 拖拽开始
    const dragStart = ref<boolean>(false)
    // 拖动数据
    const dragInfo = reactive<{ disX: number, disY: number }>({
      disX: 0,
      disY: 0
    })
    // 拖拽拖动
    const dragMousemove = (ev: MouseEvent): void => {
      if (!dragStart.value || !dialogDom.value) return
      dialogDom.value.style.left = `${ev.x - dragInfo.disX}px`
      dialogDom.value.style.top = `${ev.y - dragInfo.disY}px`
    }
    // 拖拽结束
    const dragMouseup = (): void => {
      dragStart.value = false
      document.removeEventListener('mousemove', dragMousemove)
      document.removeEventListener('mouseup', dragMouseup)
    }
    // 拖拽开始
    const dragMousedown = (ev: MouseEvent): void => {
      if (!dialogDom.value) return
      dragStart.value = true
      const rect: DOMRect = dialogDom.value?.getBoundingClientRect()
      dragInfo.disX = ev.x - rect.x
      dragInfo.disY = ev.y - rect.y
      document.addEventListener('mousemove', dragMousemove)
      document.addEventListener('mouseup', dragMouseup)
    }

    watch(() => props.visible, (val: boolean): void => {
      disableAllowScroll(val)
      if (!val) {
        closeDialog()
      } else {
        currentVisible.value = true
      }
    })
    watch(currentVisible, (val: boolean) => {
      if (val) {
        document.addEventListener('keydown', handleEscClose)
      } else {
        document.removeEventListener('keydown', handleEscClose)
      }
      ctx.emit('update:visible', val)
    })

    onBeforeUnmount(() => {
      document.removeEventListener('keydown', handleEscClose)
    })

    return {
      zIndex,
      dialogInnerStyle,
      dialogClose,
      dialogDom,
      currentVisible,
      closeDialog,
      dragMousedown
    }
  }
})
</script>
